package com.ml.arrecegor.climaplanetario.model.builder;

import java.util.Collections;
import java.util.List;

import com.ml.arrecegor.climaplanetario.model.EstadosClimaticos;
import com.ml.arrecegor.climaplanetario.model.Posicion;
import com.ml.arrecegor.climaplanetario.model.Pronostico;
import com.ml.arrecegor.climaplanetario.model.PronosticoCuantificado;

public class PronosticoBuilder {

	private static final int MARGEN_ERROR_NUMERICO = 6;

	public static Pronostico build(List<Posicion> posiciones) {
		
		if (evaluarSequia(posiciones)) {
			return new Pronostico(EstadosClimaticos.SEQUIA);
		}
	
		if ( evaluarCondiciones(posiciones)){
			return new Pronostico(EstadosClimaticos.CONDICIONES);
		}
		double lluvia = evaluarLLuvia(posiciones);
		if (lluvia > 0){
			return new PronosticoCuantificado(EstadosClimaticos.LLUVIA, lluvia);
		}
		return new Pronostico(EstadosClimaticos.INDEFINIDO);
		
	}

	private static int evaluarLLuvia(List<Posicion> posiciones) {
		//1. Analisis de cuadrante.
		//El poligono va a pasar por el sol (punto 0,0), si al menos dos posiciones estan en los cuadrantes opuestos
		if (sinCuadrantesOpuestos(posiciones)){
			return 0;
		}
		//2. calculo de perimetro.
		//2.1 Ordernar las posciones.
		Collections.sort(posiciones);
		//2.2 Calcular y acumalar el valor de lo vectores del poligono.
		return calcularPerimetroPoligono(posiciones);
	}

	private static int calcularPerimetroPoligono(List<Posicion> posiciones) {
		int perimetro = 0;
		for (int i = 0; i < posiciones.size(); i++) {
			Posicion pos1 = posiciones.get(i);
			Posicion pos2;
			//último elemento pos2 es el primero, sino es el proximo.
			if (i == (posiciones.size()-1)) {
				pos2 = posiciones.get(0); 
			} else {
				pos2 = posiciones.get(i + 1);
			}
			perimetro =+calcularPerimetroPoligono(pos1, pos2);
		}
		return perimetro;
	}

	private static int calcularPerimetroPoligono(Posicion pos1, Posicion pos2) {
		double catetoX2 = Math.pow(pos1.getX() + pos2.getX(), 2);
		double catetoY2 = Math.pow(pos1.getY() + pos2.getY(), 2);
		return (int) Math.sqrt(catetoX2 + catetoY2);
	}

	
	private static boolean sinCuadrantesOpuestos(List<Posicion> posiciones) {
		if (posiciones.size() < 2) {
			return true;
		}
		return sinCuadrantesOpuestos(posiciones.get(0), posiciones.subList(1, posiciones.size()));
	}

	private static boolean sinCuadrantesOpuestos(Posicion pos, List<Posicion> posiciones) {
		if (posiciones.size() < 2) {
			return true;
		}
		
		for (Posicion posicion : posiciones) {
			if ((Math.signum(posicion.getX()) != Math.signum(pos.getX())) && 
				(Math.signum(posicion.getY()) != Math.signum(pos.getY()))) {
				return false;
			}
		}
	
		return sinCuadrantesOpuestos(posiciones.get(0), posiciones.subList(1, posiciones.size()));
		
	}

	private static boolean evaluarCondiciones(List<Posicion> posiciones) {
		//1. Validar al menos tres posiciones.
		if (posiciones.size() < 3) {
			return false;
		}
		//2. Calular recta entre los dos primeras posiciones.
		//2.1 Calcular pendiente de la recta (a).
		double x1 = posiciones.get(0).getX();
		double x2 = posiciones.get(1).getX();
		double y1 = posiciones.get(0).getY();
		double y2 = posiciones.get(1).getY();
		double a = (x1 - x2) / (y1- y2);
		//2.2 calcular termino lineas (b)
		double b = y1 - a*x1;
		
		//3. Valiar si el resto de los puntos pertenence a la recta.
		List<Posicion> posRestantes = posiciones.subList(2, posiciones.size());
		for (Posicion posicion : posRestantes) {
			Double posY= (a*posicion.getX()) + b;
			if (Math.abs((posicion.getY() - posY.intValue())) > MARGEN_ERROR_NUMERICO) {
				return false; 
			}
		}
		return true;
	}

	private static boolean evaluarSequia(List<Posicion> posiciones) {
		double anguloAux = (posiciones.get(0).getAngulo() % 180);
		for (Posicion posicion : posiciones) {
			if ((posicion.getAngulo() % 180) != anguloAux) {
				return false;
			}
		}
		return true;
	}

}
